"""
RagFlow 客户端 GUI 批量解析选项卡组件
"""

import os
from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
    QLabel, QLineEdit, QPushButton, QFileDialog,
    QListWidget, QMessageBox, QProgressDialog, QSpinBox,
    QComboBox, QCheckBox, QGroupBox
)

from ragflow_client.api import RagFlowAPI
from ragflow_client.config import RagFlowConfig
from ragflow_client.utils.logger import get_logger

logger = get_logger(__name__)


class BatchParseTabWidget(QWidget):
    """批量解析选项卡组件"""
    
    # 定义信号
    log_message = Signal(str)
    parse_result = Signal(int, int)
    show_datasets_signal = Signal()
    
    def __init__(self, parent=None):
        """初始化批量解析选项卡组件"""
        super().__init__(parent)
        self.parent = parent
        self.current_thread = None
        self.setup_ui()
        
        # 初始化加载知识库列表
        self.refresh_kb_list()
    
    def setup_ui(self):
        """创建界面"""
        # 创建布局
        layout = QGridLayout(self)
        
        # 知识库选择
        layout.addWidget(QLabel("目标知识库:"), 0, 0)
        
        kb_layout = QHBoxLayout()
        layout.addLayout(kb_layout, 0, 1)
        
        # 创建知识库下拉框
        self.kb_combo = QComboBox()
        self.kb_combo.setMinimumWidth(300)  # 设置最小宽度
        self.kb_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents)  # 根据内容调整大小
        kb_layout.addWidget(self.kb_combo)
        
        # 刷新知识库列表按钮
        refresh_kb_button = QPushButton("刷新列表")
        refresh_kb_button.clicked.connect(self.refresh_kb_list)
        kb_layout.addWidget(refresh_kb_button)
        
        # 添加弹性空间
        kb_layout.addStretch()
        
        # 隐藏的知识库ID和名称字段，用于存储选中的知识库信息
        self.kb_id = ""
        self.kb_name = ""
        
        # 解析器选择
        layout.addWidget(QLabel("解析器:"), 1, 0)
        self.parser_id_edit = QLineEdit()
        self.parser_id_edit.setText("naive")
        layout.addWidget(self.parser_id_edit, 1, 1)
        
        # 过滤选项组
        filter_group = QGroupBox("过滤选项")
        filter_layout = QVBoxLayout(filter_group)
        
        # 是否已解析选项
        parsed_layout = QHBoxLayout()
        self.parsed_checkbox = QCheckBox("仅显示已解析文档")
        self.parsed_checkbox.setChecked(False)
        parsed_layout.addWidget(self.parsed_checkbox)
        
        self.unparsed_checkbox = QCheckBox("仅显示未解析文档")
        self.unparsed_checkbox.setChecked(False)
        parsed_layout.addWidget(self.unparsed_checkbox)
        
        # 连接复选框信号
        self.parsed_checkbox.stateChanged.connect(self.on_parsed_checkbox_changed)
        self.unparsed_checkbox.stateChanged.connect(self.on_unparsed_checkbox_changed)
        
        filter_layout.addLayout(parsed_layout)
        
        # 搜索框
        search_layout = QHBoxLayout()
        search_layout.addWidget(QLabel("搜索:"))
        self.search_edit = QLineEdit()
        self.search_edit.setPlaceholderText("输入文件名进行搜索")
        search_layout.addWidget(self.search_edit)
        
        search_button = QPushButton("搜索")
        search_button.clicked.connect(self.search_documents)
        search_layout.addWidget(search_button)
        
        filter_layout.addLayout(search_layout)
        
        layout.addWidget(filter_group, 2, 0, 1, 2)
        
        # 批次大小
        layout.addWidget(QLabel("批次大小:"), 3, 0)
        self.batch_size_spin = QSpinBox()
        self.batch_size_spin.setRange(1, 100)
        self.batch_size_spin.setValue(10)
        layout.addWidget(self.batch_size_spin, 3, 1, alignment=Qt.AlignLeft)
        
        # 文档列表
        layout.addWidget(QLabel("文档列表:"), 4, 0)
        
        # 创建文档列表
        self.documents_list = QListWidget()
        layout.addWidget(self.documents_list, 5, 0, 1, 2)
        
        # 按钮布局
        button_layout = QHBoxLayout()
        layout.addLayout(button_layout, 6, 0, 1, 2)
        
        # 刷新文档列表按钮
        refresh_button = QPushButton("刷新文档列表")
        refresh_button.clicked.connect(self.refresh_document_list)
        button_layout.addWidget(refresh_button)
        
        # 解析知识库全部文档按钮
        parse_button = QPushButton("解析知识库全部文档")
        parse_button.clicked.connect(self.parse_documents)
        button_layout.addWidget(parse_button)
        
        # 添加弹性空间
        button_layout.addStretch()
    
    def on_parsed_checkbox_changed(self, state):
        """处理已解析复选框状态变化"""
        if state == Qt.Checked:
            self.unparsed_checkbox.setChecked(False)
    
    def on_unparsed_checkbox_changed(self, state):
        """处理未解析复选框状态变化"""
        if state == Qt.Checked:
            self.parsed_checkbox.setChecked(False)
    
    def refresh_kb_list(self) -> None:
        """刷新知识库列表"""
        # 获取主窗口中的配置
        if not hasattr(self.parent, "get_config"):
            QMessageBox.critical(self, "错误", "无法获取配置信息")
            return
        
        config = self.parent.get_config()
        
        # 验证API配置
        if not config.api_url:
            QMessageBox.critical(self, "错误", "请先在配置页面设置API地址")
            return
        
        if not config.api_key:
            QMessageBox.critical(self, "错误", "请先在配置页面设置API密钥")
            return
        
        # 创建API客户端
        api = RagFlowAPI(config)
        
        # 显示加载中
        self.kb_combo.clear()
        self.kb_combo.addItem("加载中...")
        self.kb_combo.setEnabled(False)
        
        # 获取知识库列表
        try:
            logger.debug(f"请求知识库列表: API地址={config.api_url}")
            success, result = api.list_datasets(page=1, page_size=100)
            
            # 打印原始响应数据
            logger.debug(f"知识库列表API响应: success={success}, result={result}")
            
            # 清空下拉框
            self.kb_combo.clear()
            
            if success:
                # 处理不同的数据结构情况
                if isinstance(result, dict):
                    # 尝试获取数据，处理可能的不同结构
                    logger.debug(f"知识库列表响应是字典类型: {result}")
                    if "data" in result:
                        data = result["data"]
                        logger.debug(f"知识库列表data字段: {data}")
                        if isinstance(data, dict) and "items" in data:
                            datasets = data["items"]
                            logger.debug(f"从data.items获取知识库列表: {len(datasets)}个知识库")
                        elif isinstance(data, list):
                            datasets = data
                            logger.debug(f"从data列表获取知识库列表: {len(datasets)}个知识库")
                        else:
                            datasets = []
                            logger.warning(f"data既不是包含items的字典也不是列表: {data}")
                    else:
                        datasets = []
                        logger.warning(f"响应中没有data字段: {result}")
                elif isinstance(result, list):
                    # 直接使用列表作为数据集
                    datasets = result
                    logger.debug(f"知识库列表响应是列表类型: {len(datasets)}个知识库")
                else:
                    datasets = []
                    logger.warning(f"响应既不是字典也不是列表: {type(result)}")
                
                if not datasets:
                    self.kb_combo.addItem("没有可用的知识库")
                    logger.warning("API返回成功但没有知识库数据")
                else:
                    # 添加知识库到下拉框
                    for dataset in datasets:
                        if isinstance(dataset, dict):
                            kb_id = dataset.get("id", "")
                            kb_name = dataset.get("name", "")
                            logger.debug(f"添加知识库到下拉框: 名称={kb_name}, ID={kb_id}")
                            # 使用知识库名称作为显示文本，知识库ID作为用户数据
                            self.kb_combo.addItem(f"{kb_name} ({kb_id})", kb_id)
                    
                    # 使用更安全的方式处理信号连接
                    # 先将信号连接到一个临时槽函数，然后再连接到实际的处理函数
                    self.kb_combo.blockSignals(True)  # 阻止信号发送
                    
                    # 连接选择变化信号
                    self.kb_combo.currentIndexChanged.connect(self.on_kb_selected)
                    
                    self.kb_combo.blockSignals(False)  # 恢复信号发送
                    
                    # 触发选择事件
                    if self.kb_combo.count() > 0:
                        self.kb_combo.setCurrentIndex(0)
                        self.on_kb_selected(0)
                        
                    logger.info(f"成功获取知识库列表，共 {len(datasets)} 个知识库")
            else:
                error_msg = f"获取知识库列表失败: {result}"
                self.kb_combo.addItem(error_msg)
                logger.error(error_msg)
                
                # 显示详细错误信息
                QMessageBox.critical(self, "API错误", 
                                    f"获取知识库列表失败\n\n"
                                    f"API地址: {config.api_url}\n"
                                    f"错误信息: {result}\n\n"
                                    f"请检查API地址是否正确，或者服务器是否可用。")
        
        except Exception as e:
            error_msg = f"获取知识库列表失败: {str(e)}"
            self.kb_combo.clear()
            self.kb_combo.addItem(error_msg)
            logger.error(error_msg)
            
            # 显示详细错误信息
            QMessageBox.critical(self, "错误", 
                                f"获取知识库列表失败\n\n"
                                f"API地址: {config.api_url}\n"
                                f"错误信息: {str(e)}\n\n"
                                f"请检查网络连接或API配置。")
        
        # 恢复下拉框状态
        self.kb_combo.setEnabled(True)
    
    def on_kb_selected(self, index: int) -> None:
        """
        处理知识库选择事件
        
        Args:
            index: 选中的索引
        """
        if index < 0:
            return
        
        # 获取选中的知识库ID
        self.kb_id = self.kb_combo.itemData(index)
        
        # 获取选中的知识库名称（去掉ID部分）
        kb_text = self.kb_combo.itemText(index)
        if "(" in kb_text and ")" in kb_text:
            self.kb_name = kb_text.split("(")[0].strip()
        else:
            self.kb_name = kb_text
        
        # 记录选中的知识库信息
        logger.debug(f"选中知识库: 名称={self.kb_name}, ID={self.kb_id}")
        
        # 检查知识库ID是否有效
        if not self.kb_id:
            logger.warning(f"选中的知识库ID为空: index={index}, text={kb_text}")
            QMessageBox.warning(self, "警告", "选中的知识库ID为空，请重新选择知识库")
            return
        
        # 刷新文档列表
        self.refresh_document_list()
    
    def refresh_document_list(self) -> None:
        """刷新文档列表"""
        # 获取知识库ID
        if not self.kb_id:
            QMessageBox.critical(self, "错误", "请先选择知识库")
            return
        
        # 获取主窗口中的配置
        if not hasattr(self.parent, "get_config"):
            QMessageBox.critical(self, "错误", "无法获取配置信息")
            return
        
        config = self.parent.get_config()
        
        # 创建API客户端
        api = RagFlowAPI(config)
        
        # 显示加载中
        self.documents_list.clear()
        self.documents_list.addItem("加载中...")
        
        # 获取文档列表
        try:
            logger.debug(f"请求文档列表: 知识库ID={self.kb_id}")
            success, result = api.list_documents(dataset_id=self.kb_id, page=1, page_size=100)
            
            # 打印原始响应数据
            logger.debug(f"文档列表API响应: success={success}, result={result}")
            
            # 清空列表框
            self.documents_list.clear()
            
            if success:
                # 处理不同的数据结构情况
                documents = []
                
                if isinstance(result, dict):
                    # 尝试获取数据，处理可能的不同结构
                    logger.debug(f"文档列表响应是字典类型: {result}")
                    if "data" in result:
                        data = result["data"]
                        logger.debug(f"文档列表data字段: {data}")
                        
                        # 处理数据结构：data.docs
                        if isinstance(data, dict) and "docs" in data:
                            documents = data["docs"]
                            logger.debug(f"从data.docs获取文档列表: {len(documents)}个文档")
                        elif isinstance(data, list):
                            documents = data
                            logger.debug(f"从data列表获取文档列表: {len(documents)}个文档")
                        else:
                            logger.warning(f"data既不是包含docs的字典也不是列表: {type(data)}")
                    else:
                        logger.warning(f"响应中没有data字段: {list(result.keys())}")
                        # 尝试直接从结果中获取文档列表
                        if "docs" in result:
                            documents = result["docs"]
                            logger.debug(f"从result.docs获取文档列表: {len(documents)}个文档")
                elif isinstance(result, list):
                    # 直接使用列表作为数据集
                    documents = result
                    logger.debug(f"文档列表响应是列表类型: {len(documents)}个文档")
                else:
                    logger.warning(f"响应既不是字典也不是列表: {type(result)}")
                
                if not documents:
                    self.documents_list.addItem("没有可用的文档")
                    logger.warning("API返回成功但没有文档数据")
                else:
                    # 记录原始文档数据
                    logger.debug(f"原始文档数据示例: {documents[0] if documents else None}")
                    
                    # 过滤文档
                    filtered_documents = self.filter_documents(documents)
                    
                    # 添加文档到列表框
                    for doc in filtered_documents:
                        if isinstance(doc, dict):
                            # 记录每个文档的字段
                            logger.debug(f"文档字段: {list(doc.keys())}")
                            
                            doc_id = doc.get("id", "")
                            doc_name = doc.get("name", "")
                            doc_status = doc.get("status", "")
                            
                            # 检查文档ID和名称
                            if not doc_id:
                                logger.warning(f"文档ID为空: {doc}")
                            if not doc_name:
                                logger.warning(f"文档名称为空: {doc}")
                            
                            # 获取文档的解析状态（run字段）
                            doc_run = doc.get("run", "UNSTART")  # 获取run字段，默认为UNSTART
                            
                            # 根据run字段确定解析状态
                            run_status_map = {
                                "UNSTART": "未解析",
                                "RUNNING": "解析中",
                                "CANCEL": "已取消",
                                "DONE": "解析成功",
                                "FAIL": "解析失败"
                            }
                            
                            # 获取状态文本，如果不在映射中则显示原始值
                            run_status_text = run_status_map.get(str(doc_run), f"未知({doc_run})")
                            
                            # 使用文档名称和解析状态作为显示文本
                            item_text = f"{doc_name} ({run_status_text})"
                            self.documents_list.addItem(item_text)
                            
                            # 存储文档ID作为项目数据
                            self.documents_list.item(self.documents_list.count() - 1).setData(Qt.UserRole, doc_id)
                            logger.debug(f"添加文档到列表: {item_text}, ID={doc_id}")
                    
                    logger.info(f"成功获取文档列表，共 {len(filtered_documents)} 个文档")
            else:
                error_msg = f"获取文档列表失败: {result}"
                self.documents_list.addItem(error_msg)
                logger.error(error_msg)
                
                # 显示详细错误信息
                QMessageBox.critical(self, "API错误", 
                                    f"获取文档列表失败\n\n"
                                    f"知识库ID: {self.kb_id}\n"
                                    f"错误信息: {result}\n\n"
                                    f"请检查API地址是否正确，或者服务器是否可用。")
        
        except Exception as e:
            error_msg = f"获取文档列表失败: {str(e)}"
            self.documents_list.clear()
            self.documents_list.addItem(error_msg)
            logger.error(error_msg)
            
            # 显示详细错误信息
            QMessageBox.critical(self, "错误", 
                                f"获取文档列表失败\n\n"
                                f"知识库ID: {self.kb_id}\n"
                                f"错误信息: {str(e)}\n\n"
                                f"请检查网络连接或API配置。")
    
    def filter_documents(self, documents):
        """
        根据过滤条件过滤文档
        
        Args:
            documents: 文档列表
            
        Returns:
            过滤后的文档列表
        """
        filtered_docs = []
        
        # 获取搜索文本
        search_text = self.search_edit.text().strip().lower()
        
        # 获取解析状态过滤条件
        only_parsed = self.parsed_checkbox.isChecked()
        only_unparsed = self.unparsed_checkbox.isChecked()
        
        logger.debug(f"过滤文档: 搜索文本='{search_text}', 仅已解析={only_parsed}, 仅未解析={only_unparsed}")
        logger.debug(f"原始文档数量: {len(documents)}")
        
        for doc in documents:
            if not isinstance(doc, dict):
                logger.warning(f"文档不是字典类型: {type(doc)}")
                continue
                
            doc_name = doc.get("name", "").lower()
            doc_status = doc.get("status", "")
            
            # 记录文档信息
            logger.debug(f"处理文档: 名称='{doc_name}', 状态='{doc_status}'")
            
            # 应用搜索过滤
            if search_text and search_text not in doc_name:
                logger.debug(f"文档 '{doc_name}' 不匹配搜索文本 '{search_text}'")
                continue
            
            # 获取文档的解析状态（run字段）
            doc_run = doc.get("run", "UNSTART")  # 默认为UNSTART（未解析）
            
            # 判断文档是否已成功解析
            is_parsed = doc_run == "DONE"  # 只有DONE状态表示解析成功
            
            # 应用解析状态过滤
            if only_parsed and not is_parsed:
                logger.debug(f"文档 '{doc_name}' 未成功解析，但仅显示已解析文档")
                continue
            
            if only_unparsed and is_parsed:
                logger.debug(f"文档 '{doc_name}' 已成功解析，但仅显示未解析文档")
                continue
            
            filtered_docs.append(doc)
        
        logger.debug(f"过滤后文档数量: {len(filtered_docs)}")
        return filtered_docs
    
    def search_documents(self):
        """搜索文档"""
        self.refresh_document_list()
    
    def parse_documents(self):
        """批量解析知识库全部文档"""
        # 检查是否选择了知识库
        if not self.kb_id:
            QMessageBox.critical(self, "错误", "请先选择知识库")
            return
            
        # 获取主窗口中的配置
        if not hasattr(self.parent, "get_config"):
            QMessageBox.critical(self, "错误", "无法获取配置信息")
            return
        
        config = self.parent.get_config()
        
        # 创建API客户端
        api = RagFlowAPI(config)
        
        # 获取解析器ID
        parser_id = self.parser_id_edit.text().strip()
        
        # 获取批次大小
        batch_size = self.batch_size_spin.value()
        
        # 创建进度对话框 - 只创建一次
        progress = QProgressDialog("正在获取文档列表...", "取消", 0, 100, self)
        progress.setWindowTitle("批量解析文档")
        progress.setWindowModality(Qt.WindowModal)
        progress.setMinimumDuration(0)
        progress.setValue(0)
        progress.setMinimumWidth(400)  # 设置最小宽度以确保文本显示完整
        progress.show()
        
        # 解析文档计数
        success_count = 0
        failed_count = 0
        total_docs = 0
        processed_docs = 0
        
        # 收集所有需要解析的文档
        all_doc_ids_to_parse = []
        all_doc_names_to_parse = []
        
        try:
            # 第一阶段：收集所有需要解析的文档
            progress.setLabelText("正在收集需要解析的文档...")
            
            # 分页获取所有文档
            page = 1
            has_more = True
            
            while has_more:
                # 检查是否取消
                if progress.wasCanceled():
                    break
                
                progress.setLabelText(f"正在获取第 {page} 页文档...")
                
                # 获取文档列表
                success, result = api.list_documents(dataset_id=self.kb_id, page=page, page_size=batch_size)
                
                if not success:
                    self.log_message.emit(f"获取第 {page} 页文档列表失败: {result}")
                    QMessageBox.critical(self, "错误", f"获取第 {page} 页文档列表失败: {result}")
                    break
                
                # 处理不同的数据结构情况
                documents = []
                
                if isinstance(result, dict):
                    if "data" in result:
                        data = result["data"]
                        
                        if isinstance(data, dict) and "docs" in data:
                            documents = data["docs"]
                            # 检查是否有更多页
                            total = data.get("total", 0)
                            current = page * batch_size
                            has_more = current < total
                        elif isinstance(data, list):
                            documents = data
                            has_more = len(documents) == batch_size
                        else:
                            logger.warning(f"data既不是包含docs的字典也不是列表: {type(data)}")
                            documents = []
                            has_more = False
                    else:
                        logger.warning(f"响应中没有data字段: {list(result.keys())}")
                        if "docs" in result:
                            documents = result["docs"]
                            # 检查是否有更多页
                            total = result.get("total", 0)
                            current = page * batch_size
                            has_more = current < total
                        else:
                            documents = []
                            has_more = False
                elif isinstance(result, list):
                    documents = result
                    has_more = len(documents) == batch_size
                
                # 更新总文档数
                total_docs += len(documents)
                
                # 处理当前页的文档
                for doc in documents:
                    # 检查是否取消
                    if progress.wasCanceled():
                        break
                    
                    # 获取文档ID和名称
                    doc_id = doc.get("id", "")
                    doc_name = doc.get("name", "")
                    
                    # 获取文档的解析状态
                    doc_run = doc.get("run", "UNSTART")
                    
                    # 只处理未解析成功的文档
                    if doc_run != "DONE":
                        all_doc_ids_to_parse.append(doc_id)
                        all_doc_names_to_parse.append(doc_name)
                
                # 进入下一页
                page += 1
            
            # 检查是否取消
            if progress.wasCanceled():
                progress.close()
                QMessageBox.information(self, "已取消", "批量解析已取消")
                return
            
            # 第二阶段：批量解析收集到的文档
            if all_doc_ids_to_parse:
                # 更新进度对话框
                total_to_parse = len(all_doc_ids_to_parse)
                progress.setMaximum(total_to_parse)
                progress.setValue(0)
                
                # 分批处理文档
                for i in range(0, total_to_parse, batch_size):
                    # 检查是否取消
                    if progress.wasCanceled():
                        break
                    
                    # 获取当前批次
                    batch_ids = all_doc_ids_to_parse[i:i+batch_size]
                    batch_names = all_doc_names_to_parse[i:i+batch_size]
                    
                    progress.setLabelText(f"正在批量解析 {len(batch_ids)} 个文档...({processed_docs}/{total_to_parse})")
                    
                    try:
                        # 解析文档
                        parse_success, parse_result = api.parse_document(
                            dataset_id=self.kb_id, 
                            document_ids=batch_ids, 
                            parser_id=parser_id
                        )
                        
                        if parse_success:
                            success_count += len(batch_ids)
                            self.log_message.emit(f"批量解析 {len(batch_ids)} 个文档成功")
                            for doc_name in batch_names:
                                self.log_message.emit(f"文档 {doc_name} 解析成功")
                        else:
                            failed_count += len(batch_ids)
                            self.log_message.emit(f"批量解析 {len(batch_ids)} 个文档失败: {parse_result}")
                    except Exception as e:
                        failed_count += len(batch_ids)
                        self.log_message.emit(f"批量解析文档失败: {str(e)}")
                    
                    # 更新处理进度
                    processed_docs += len(batch_ids)
                    progress.setValue(processed_docs)
            
            # 关闭进度对话框
            progress.close()
            
            # 显示结果
            if progress.wasCanceled():
                QMessageBox.information(self, "已取消", f"批量解析已取消，成功: {success_count}，失败: {failed_count}")
            elif failed_count == 0 and success_count == 0:
                QMessageBox.information(self, "完成", "没有需要解析的文档")
            elif failed_count == 0:
                QMessageBox.information(self, "成功", f"所有文档解析成功，共 {success_count} 个文档")
            else:
                QMessageBox.warning(self, "警告", f"部分文档解析失败，成功: {success_count}，失败: {failed_count}")
            
            # 发送解析结果信号
            self.parse_result.emit(success_count, failed_count)
            
        except Exception as e:
            progress.close()
            error_msg = f"批量解析过程中发生错误: {str(e)}"
            self.log_message.emit(error_msg)
            QMessageBox.critical(self, "错误", error_msg)
        
        # 刷新文档列表
        self.refresh_document_list()
